package cn.demoncat.util.mysql.util;

import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.io.CloseUtil;
import cn.demoncat.util.io.IoUtil;
import cn.demoncat.util.lang.constant.StatusConstant.Common;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.Result;
import cn.demoncat.util.mysql.entity.DbGenParam;
import cn.demoncat.util.mysql.entity.MysqlColumn;
import cn.demoncat.util.mysql.entity.MysqlTable;
import cn.demoncat.util.web.ServletUtil;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.List;

/**
 * 数据库文档Word生成工具
 *
 * @author 延晓磊
 *
 * @since 2017年8月11日
 */
public class DocWordGenUtil {

	// 样式模板
	private static final String STYLE_DOCX_PATH = "docx/style.docx";
	// 样式
	private static final CTStyles WORD_STYLES = getWordStyle();
	// 列宽(8300)
	private static final int W1 = 1800;
	private static final int W2 = 1400;
	private static final int W3 = 800;
	private static final int W4 = 800;
	private static final int W5 = 900;
	private static final int W6 = 2600;
	// 表头
	private static final String[] HEAD = { "字段", "类型", "KEY", "Null", "默认值", "说明" };
	// 表头文字大小
	private static final int HEAD_FONT_SIZE = 11;
	// 表标题的样式ID：WPS标题1="2"
	private static final String TABLE_TITLE_STYLE = "3";

	/**
	 * 功能描述：测试模板文档的样式，以便查找样式ID来设置段落
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年8月12日
	 */
	public static void checkStyles() {
		// 获取模板文档的所有样式
		for (CTStyle e: WORD_STYLES.getStyleList()) {
			// 查看样式的XML详情，w:styleId 即样式ID，可以设置给段落 XWPFParagraph
			System.out.println(e);
		}
	}
 
	/**
	 * 本地生成文档
	 * 
	 * @param out		输出目录，后缀/
	 * @param db		数据库信息
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年7月5日
	 */
	public static void localGen(String out, DbGenParam db) {
		if (!out.endsWith(StringConstant.SEPARATOR)) {
			out = out + StringConstant.SEPARATOR;
		}
		// 查询数据库表
		List<MysqlTable> tableInfos = DbGenUtil.listTableInfos(db, DbGenUtil.listTables(db, "*"));
		// 生成文档
		OutputStream output = null;
		try {
			output = new FileOutputStream(out + db.getName() + ".doc");
			genWord(db.getName(), tableInfos, output);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			CloseUtil.close(output);
		}
	}
	
	/**
	 * WEB服务生成文档
	 * 
	 * @param check		1校验，0生成
	 * @param response
	 * @param db		数据库信息
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年7月5日
	 */
	public static Result webGen(String check, HttpServletResponse response, DbGenParam db) {
		// 查询表信息集
		List<MysqlTable> tableInfos = DbGenUtil.listTableInfos(db, DbGenUtil.listTables(db, "*"));
		// 校验
		if (Common.YES.equals(check)) {
			if (!tableInfos.isEmpty()) {
				return Result.SUCCESS;
			}else {
				return Result.error("数据库中没有可用的表");
			}
		}
		// 下载数据库文档
		genWord(db.getName(), tableInfos, ServletUtil.getStreamOut(response, db.getName() + ".docx"));
		// 下载成功返回null，避免SpringMvc二次输出响应
		return null;
	}
	
	
	/**
	 * 生成数据库文档
	 * 
	 * @param dbName		数据库名
	 * @param tableInfos	表信息集
	 * @param out			输出流
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年7月3日
	 */
	private static void genWord(String dbName, List<MysqlTable> tableInfos, OutputStream out) {
		// 创建文档
		XWPFDocument xDocument = new XWPFDocument();
		// 获取文档的样式
        XWPFStyles newStyles = xDocument.createStyles();
        // 设置文档的样式为模板文档的样式
        newStyles.setStyles(WORD_STYLES);
		// 创建文档标题
		writeDocTitle(dbName,xDocument);
		// 遍历数据库表集
		List<MysqlColumn> columns;
		int i;
		XWPFTable xTable;
		CTTblPr tbPr;
		for (MysqlTable table : tableInfos) {
			// 创建表标题
			writeTableTitle(xDocument, table);
			// 获取表字段
			columns = table.getColumns();
			// 创建表格：行数，列数
			xTable = xDocument.createTable(columns.size() + 1, 6);
			// 获取表格的属性
			tbPr = xTable.getCTTbl().addNewTblPr();
			// 表格居中
			tbPr.addNewJc().setVal(STJc.CENTER);
			// 创建表头
			i = 0;
			writeTableHead(xTable, i);
			i = 1;
			for (MysqlColumn column : columns) {
				// 创建表单元格
				writeTableCell(column, xTable, i);
				i++;
			}
		}
		// 输出
		try {
			xDocument.write(out);
		} catch (IOException e) {
			throw new IoRuntimeException("导出数据库文档失败", e);
		}finally {
			try {
				xDocument.close();
			} catch (IOException e) {
				// 无操作
			}
		}
	}

	/**
	 * 功能描述：创建文档的标题
	 *
	 * @param xDocument
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static void writeDocTitle(String dbName, XWPFDocument xDocument) {
		// 创建段落
		XWPFParagraph xp = xDocument.createParagraph();
		// 段落居中
		xp.setAlignment(ParagraphAlignment.CENTER);
		// 段落文本区
		XWPFRun r = xp.createRun();
		r.setText(dbName + " 数据库文档");
		r.setFontSize(20);
		r.setBold(true);
	}

	/**
	 * 功能描述：创建表的标题
	 *
	 * @param xDocument
	 * @param table
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static void writeTableTitle(XWPFDocument xDocument, MysqlTable table) {
		// 创建空段落行
		xDocument.createParagraph();
		// 创建段落
		XWPFParagraph xp = xDocument.createParagraph();
		// 段落居左
		xp.setAlignment(ParagraphAlignment.LEFT);
		// 段落样式-标题1(须创建并读取模板文档，并在文档中有标题1的内容，然后指定标题1的样式ID)
		xp.setStyle(TABLE_TITLE_STYLE);
		// 段落文本区
		XWPFRun r = xp.createRun();
		r.setText(table.getComment() == null || table.getComment().length() == 0 ? table.getName() : table.getComment() + "  " + table.getName());
	}

	/**
	 * 功能描述：创建表的表头
	 *
	 * @param xTable
	 * @param i
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static void writeTableHead(XWPFTable xTable, int i) {
		// 将段落填充到单元格，实现单元格字段样式控制
		XWPFParagraph xp;
		XWPFRun r;
		for (int k = 0; k < HEAD.length; k++) {
			// 获取单元格的段落
			xp = xTable.getRow(i).getCell(k).getParagraphs().get(0);
			// 段落居中
			xp.setAlignment(ParagraphAlignment.CENTER);
			// 段落文本区
			r = xp.createRun();
			r.setFontSize(HEAD_FONT_SIZE);
			r.setBold(true);
			r.setText(HEAD[k]);
		}
		// 列宽
		colWidth(xTable, i);
	}

	/**
	 * 功能描述：创建表的单元格
	 *
	 * @param column
	 * @param xTable
	 * @param i
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static void writeTableCell(MysqlColumn column, XWPFTable xTable, int i) {
		// 表体
		xTable.getRow(i).getCell(0).setText(column.getName());
		xTable.getRow(i).getCell(1).setText(column.getFullType());
		xTable.getRow(i).getCell(2).setText(column.getKeyType());
		xTable.getRow(i).getCell(3).setText(column.getIsNullable());
		xTable.getRow(i).getCell(4).setText(column.getValue());
		xTable.getRow(i).getCell(5).setText(column.getComment());
		// 列宽
		colWidth(xTable, i);
	}

	/**
	 * 功能描述：设置单元格列宽
	 *
	 * @param xTable
	 * @param i
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static void colWidth(XWPFTable xTable, int i) {
		// 获取单元格(原型)
		CTTc ctTc = xTable.getRow(i).getCell(0).getCTTc();
		// 获取单元格属性(Pr)
		CTTcPr tcPr = ctTc.addNewTcPr();
		// 设置单元格属性 - 列宽
		tcPr.addNewTcW().setW(BigInteger.valueOf(W1));
		xTable.getRow(i).getCell(1).getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(W2));
		xTable.getRow(i).getCell(2).getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(W3));
		xTable.getRow(i).getCell(3).getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(W4));
		xTable.getRow(i).getCell(4).getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(W5));
		xTable.getRow(i).getCell(5).getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(W6));
	}

	/**
	 * 功能描述：读取模板文档，获取样式
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月11日
	 */
	private static CTStyles getWordStyle() {
		// 读取模板文档，创建文档
		try (InputStream styleFile = IoUtil.getInputStreamByClassPath(STYLE_DOCX_PATH); XWPFDocument doc = new XWPFDocument(styleFile)) {
			// 获取模板文档的整体样式
			return doc.getStyle();
		} catch (XmlException | IOException e) {
			throw new RuntimeException("读取模板文档失败", e);
		}
	}
}
